package com.well.register.netty;

import com.well.register.netty.handle.ClusterConnHandle;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;
import io.netty.handler.timeout.IdleStateHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.SocketAddress;

public class ClusterClient {
    Logger logger = LoggerFactory.getLogger(ClusterClient.class);
    private static EventLoopGroup eventLoopGroup = new NioEventLoopGroup();

    private static Bootstrap bootstrap = new Bootstrap();

    static {
        bootstrap.group(eventLoopGroup)
                .channel(NioSocketChannel.class);
    }

    private static class SimpleClientInitializer extends ChannelInitializer<SocketChannel> {
        public ChannelPipeline pipeline;

        @Override
        protected void initChannel(SocketChannel socketChannel) {
            pipeline = socketChannel.pipeline();
            pipeline.addLast(new IdleStateHandler(0, 4, 0));
            pipeline.addLast(new ObjectEncoder());
            pipeline.addLast(new ObjectDecoder(Integer.MAX_VALUE,
                    ClassResolvers.cacheDisabled(null)));
            pipeline.addLast(new ClusterConnHandle());
        }
    }

    /**
     * 添加 handle
     *
     * @param handler
     * @return
     */
    private SimpleClientInitializer addHandleOfPipeline(ChannelHandler handler) {
        SimpleClientInitializer simpleClientInitializer = new SimpleClientInitializer();
        if (simpleClientInitializer.pipeline != null && handler != null) {
            simpleClientInitializer.pipeline.addLast(handler);
        }
        return simpleClientInitializer;
    }

    /**
     * 添加处理器
     *
     * @param handler
     */
    public void addHandle(ChannelHandler handler) {
        bootstrap.handler(addHandleOfPipeline(handler));
    }

    /**
     * 创建 Channel
     *
     * @param socketAddress
     * @return
     */
    private Channel doConnect(SocketAddress socketAddress) {
        final ChannelFuture channelFuture = bootstrap.connect(socketAddress);
        channelFuture.addListener((ChannelFutureListener) futureListener -> {
            if (!channelFuture.isSuccess()) {
                logger.info("Failed to connect to server," + socketAddress.toString() + ", try connect after 5s");
            }
        });
        return channelFuture.channel();
    }

    /**
     * @param handler
     * @return
     */
    public Channel createChannel(ChannelHandler handler, SocketAddress socketAddress) {
        addHandle(handler);
        return doConnect(socketAddress);
    }
}
